#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/random.h>

#include "if_media.h"

#include <net80211/ieee80211_var.h>
#include <net80211/if_athproto.h>

/* verification failed handle */
/* XXXPLM: Add specific statistics for mesh */				
#define	IEEE80211_VERIFICATION_FAILED(_ret, _reason) do {\
	if (_ret) {\
		switch (_reason) {\
			case MESH_SECURITY_FAILED_VERIFICATION: \
				vap->iv_stats.is_rx_assoc_badwpaie++;\
				break;	\
			case MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE:	\
				vap->iv_stats.is_rx_assoc_badwpaie++;	\
				break;	\
		}\
		ieee80211_plm_smstep(ni,OPN_RJCT);\
		return;	\
	}\
} while (0)

/* Save _type ie to appointed location */
#define IEEE80211_PLM_SAVEIE(_dst, _src) do {\
	if (_src != NULL) {\
		ieee80211_saveie(&_dst, _src);\
	} else if (_dst != NULL) {\
		FREE(_dst, M_DEVBUF);\
		_dst = NULL;\
	}\
} while (0)

const char *ieee80211_plmstate_name[7] = {
	"IDLE",			/* IEEE80211_PLM_S_IDLE */
	"LISTEN",		/* IEEE80211_PLM_S_LISTEN */
	"OPN_SNT",		/* IEEE80211_PLM_S_OPN_SNT */
	"CNF_RCVD",		/* IEEE80211_PLM_S_CNF_RCVD */
	"OPN_RCVD",		/* IEEE80211_PLM_S_OPN_RCVD */
	"ESTAB",		/* IEEE80211_PLM_S_ESTAB */
	"HOLDING"		/* IEEE80211_PLM_S_HOLDING */
};
EXPORT_SYMBOL(ieee80211_plmstate_name);

/* inputs for pmk-selection and role selection */
struct ieee80211_peermsaparms {
	u_int8_t msa_phandshakectl;	/* Handshake control in received msa ie */
	u_int8_t msa_pmaid[IEEE80211_ADDR_LEN];	/* MA ID in msa ie */
	/* XXX: simply copy of corresponding rsn params? */
	u_int8_t msc_pmsecconf;	/* Mesh security configuration in received msc ie */
	u_int8_t msa_isselector; /* result of selector selection, 1 for local and 0 for peer */
	u_int8_t validlocalkey;
	u_int8_t cachedpeerkey;
	u_int8_t pchosenpmk[IEEE80211_PMK_LEN];		/* chosen pmk through pmk selection */
};

/*
 * 802.11s/MSC information element.
 */
struct ieee80211_ie_msc {
	u_int8_t msc_id;			/* IEEE80211_ELEMID_MSC ieee80211.h*/
	u_int8_t msc_len;			/* length in bytes */
	u_int8_t msc_mkddid[6];		/* MKD Domain ID */
	u_int8_t msc_config;		/* Mesh Security Configuration */
} __packed;

/*
 * 802.11s/MSA information element (maximally sized).
 */
struct ieee80211_ie_msa {
	u_int8_t msa_id;			/* IEEE80211_ELEMID_MSA		ieee80211.h */
	u_int8_t msa_len;		/* length in bytes */
	u_int8_t msa_hsctl;		/* MSA Handshake control */
	u_int8_t msa_maid[IEEE80211_ADDR_LEN];		/* Selected MA ID */
	u_int8_t msa_selakm[4];	/* Selected AKM Suite */
	u_int8_t msa_selucipher[4];	/* Selected Pairwise Cipher Suite */
	u_int16_t msa_chosenpmk[8];		/* Chosen PMK */
	u_int32_t msa_lnonce[8];	/* Local Nonce */
	u_int32_t msa_pnonce[8];	/* Peer Nonce */
	u_int8_t msa_mkdid[8];		/* Optional Para: MKD-ID */
	u_int8_t msa_khtrans[6];	/* Optional Para: Key Holder Transport List */
	u_int8_t msa_pmkmkdname[18];	/* Optional Para: PMK-MKDName */
	u_int8_t msa_mkdnasid[18];	/* Optional Para: MKD-NAS-ID */
	u_int8_t msa_gtk[32];		/* Optional Para: GTK Data */
} __packed;

/* PLM IEs */
struct ieee80211_elems {
	u_int8_t *ssid;
	u_int8_t *rates;
	u_int8_t *xrates;
	u_int8_t *rsnie;
	u_int8_t *msaie;
	u_int8_t *mscie;
};
	
/**
 * ieee80211_plm_isselector - determine who is selector mp
 * called before plm process, communication peers calculate the selector then determine 
 * selected pairwise and key mgmt suite filled in msa ie. 
 * The MP is the Selector MP if its MAC address is numerically 
 * larger than that of the candidate peer MP.
 * @peeraddr: peer mp's mac address
 * @localaddr: local mp's mac address
 * Returns: 1 - local is the selector, 0 - peer is the selector
 */
int 
ieee80211_plm_isselector(u_int8_t *peeraddr, u_int8_t *localaddr)
{
	int i;
	i = memcmp(peeraddr, localaddr, IEEE80211_ADDR_LEN);
	return ((i < 0) ? 1 : 0);
}

/**
 * ieee80211_plm_parseies - parse ies in plm messages
 * recording start points of each ies into elems structure
 * @vap: global data
 * @elems: pointers to ies
 * @wh: header field of received message
 * @frm: beginning of processing data
 * @efrm: end of processing data 
 * Returns: 0 on success
 */
static int
ieee80211_plm_parseies(struct ieee80211vap *vap, struct ieee80211_elems *elems, 
	struct ieee80211_frame *wh, u_int8_t *frm, u_int8_t *efrm)
{
	/*XXXPLM: check the existence of rsn, msa, msc ies */
	elems->ssid = elems->rates = elems->xrates 
		= elems->rsnie = elems->mscie = elems->msaie = NULL;
	while (frm < efrm) {
		switch (*frm) {
		case IEEE80211_ELEMID_SSID:
			elems->ssid = frm;
			break;
		case IEEE80211_ELEMID_RATES:
			elems->rates = frm;
			break;
		case IEEE80211_ELEMID_XRATES:
			elems->xrates = frm;
			break;
		/* XXX verify only one of RSN and WPA ie's? */
		case IEEE80211_ELEMID_RSN:
			if (vap->iv_flags & IEEE80211_F_WPA2)
				elems->rsnie = frm;
			else
				IEEE80211_DPRINTF(vap,
					IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
					"[%s] ignoring RSN IE in association request\n",
					ether_sprintf(wh->i_addr2));
			break;
		case IEEE80211_ELEMID_MSC:
			if (vap->iv_flags & IEEE80211_F_WPA2)
				elems->mscie = frm;
			else
				IEEE80211_DPRINTF(vap,
					IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
					"[%s] ignoring RSN IE in association request\n",
					ether_sprintf(wh->i_addr2));
			break;
		case IEEE80211_ELEMID_MSA:
			if (vap->iv_flags & IEEE80211_F_WPA2)
				elems->msaie = frm;
			else
				IEEE80211_DPRINTF(vap,
					IEEE80211_MSG_ASSOC | IEEE80211_MSG_WPA,
					"[%s] ignoring RSN IE in association request\n",
					ether_sprintf(wh->i_addr2));
			break;
		default:
			/* unknown ies, no debug messages*/
			return 1;
			break;
		}
		frm += frm[1] + 2;
	}
	/* the length field in ies has errors */
	if (frm > efrm)
		return 1;
	return 0;
}

/**
 * ieee80211_plm_verifyopen - verifying ies in open message (open first)
 * @vap: global data
 * @elems: pointers to ies
 * @rsn_parm: local msa configuration
 * @wh: header field of received message
 * Returns: 0 on success
 */
static int
ieee80211_plm_verifyopen(struct ieee80211vap *vap, struct ieee80211_elems *elems,
	const struct ieee80211_frame *wh, struct ieee80211_rsnparms *rsn_parm)
{
	struct ieee80211_msaparms *msa_parm = &rsn_parm->msaparms;
	struct ieee80211_ie_msc *msc = (struct ieee80211_ie_msc *)elems->mscie;
	struct ieee80211_ie_msa *msa = (struct ieee80211_ie_msa *)elems->msaie;
	u_int8_t res;

	/* nothing to verify for rate and xrate, so begin with rsn ie */
	
	/* Verify that the local MP supports the peer MP's group cipher 
	suite as indicated in the RSNIE received in the peer link open 
	message.  Further, verify that the pairwise cipher suite list 
	and AKM suite list in the received RSNIE each contain at least 
	one entry that is also supported by the local MP.*/
	res = ieee80211_parse_rsn(vap, elems->rsnie, rsn_parm, wh);
	if (res)
		return 1;

	/* msc ie */
	memcpy(msa_parm->msc_pmkddid, msc->msc_mkddid, 6);
	msa_parm->msc_pmsecconf = msc->msc_config;
	/* Verify that the "Default Role Negotiation" field included 
	in the MSCIE of the peer link open message is identical to the 
	value included in the local MP's MSCIE in Beacon frames and Probe 
	Response frames.*/
	res = (msa_parm->msc_pmsecconf ^ msa_parm->msc_msecconf) 
			& IEEE80211_MSC_DEFTROLENEGOT;
	if (res)
		return 1;
	
	/* msa ie */
	msa_parm->msa_phandshakectl = msa->msa_hsctl;
	if (!msa_parm->msa_isselector) { 
		msa_parm->msa_pucastcipher = 0;
		msa_parm->msa_pucastcipher |= (1 << rsn_cipher(msa->msa_selucipher, &rsn_parm->rsn_ucastkeylen));
		msa_parm->msa_pkeymgmt = 0;
		msa_parm->msa_pkeymgmt |= rsn_keymgmt(msa->msa_selakm);
	}
	/* If the local MP is not the Selector MP, verify that the AKM 
	suite and pairwise cipher suite selected in the MSAIE are among 
	those supported by the local MP. */
	res = 0;
	res = msa_parm->msa_pucastcipher & rsn_parm->rsn_ucastcipherset;
	if (!res)
		return 1;
	res = 0;
	res = msa_parm->msa_pkeymgmt & rsn_parm->rsn_keymgmtset;
	if (!res)
		return 1;
	/* Verify that it wishes to establish a link with the peer MP 
	that sent the peer link open message, based on the policies 
	advertised in the peer link open message, and, if present, the 
	Selector MP's choice of AKM suite and pairwise cipher suite.  
	*/

	return 0;
}

/**
 * ieee80211_plm_verifyopen1 - verifying ies in open message (confirm first)
 * @ni: global data
 * @elems: pointers to ies
 * @rsn_parm: local msa configuration
 * @wh: header field of received message
 * Returns: 0 on success
 */
static int
ieee80211_plm_verifyopen1(struct ieee80211_node *ni, struct ieee80211_elems *elems,
	const struct ieee80211_frame *wh, struct ieee80211_rsnparms *rsn_parm)
{
	struct ieee80211_msaparms *msa_parm = &rsn_parm->msaparms;
	u_int8_t res;
/* RSNIE is identical to the RSNIE included in the peer link confirm 
message received from the candidate peer MP, except the PMKID list. */
/* XXXPLM: Add PMKID List field */
	res = memcmp(elems->rsnie, ni->ni_rsn_ie, elems->rsnie[1]);
	if (res)
		return 1;
/* MSCIE is identical to the MSCIE included in the peer link confirm 
message received from the candidate peer MP.*/
	res = memcmp(elems->mscie, ni->ni_msc_ie, elems->mscie[1]);
	if (res)
		return 1;
/* In the MSAIE, Handshake Control field is identical to that included 
in the received peer link confirm message. If the candidate peer MP 
is the selector MP, the values in the Selected AKM Suite and Selected
Pairwise Cipher Suite fields are identical to the values received in 
peer link confirm message.*/
	if (msa_parm->msa_isselector)
		res = memcmp(elems->msaie, ni->ni_msa_ie, 3);
	else 
		res = memcmp(elems->msaie, ni->ni_msa_ie, 11);
	if (res)
		return 1;

	return 0;
}

/**
 * ieee80211_plm_ksinputs - determine the value of "valid local key" 
 * and "cached peer key" with the information contained in the 
 * pmkid field in rsn ie and other key selection inputs
 * @ni: global data, using ni->ni_rsn.msaparms to store the result 
 * @elems: pointers to ies to be parsed
 * Returns: a 5 bits integer representing table input
 */
static u_int8_t
ieee80211_plm_ksinputs(const struct ieee80211_msaparms *msa_parm)
{
	/* input mask:
	 * 		bit 4: valid-local-key
	 * 		bit 3: cached-peer-key
	 * 		bit 2: connect to mkd(peer)
	 * 		bit 1: connect to mkd(local)
	 * 		bit 0: is selector?
	 */
#define		VALIDLOCALKEY		00010000 
#define		CACHEDPEERKEY		00001000
#define 	CONNTOMKDPEER		00000100
#define		CONNTOMKDLOCAL		00000010
#define		LOCALISSELECT		00000001
	u_int8_t tableinputs = 0;
	u_int8_t vlk, cpk;
	/* XXXPLM: valid local key */
	vlk = 0;
	if(vlk)
		tableinputs |= VALIDLOCALKEY;

	/* XXXPLM: cached peer key */
	cpk = 0;
	if(cpk)
		tableinputs |= CACHEDPEERKEY;

	/* connect to mkd, peer */
	if (msa_parm->msc_pmsecconf & IEEE80211_MSC_CONNTOMKD)
		tableinputs |= CONNTOMKDPEER;

	/* connect to mkd, local */
	if (msa_parm->msc_msecconf & IEEE80211_MSC_CONNTOMKD)
		tableinputs |= CONNTOMKDLOCAL;

	/* local mp is selector */
	if (msa_parm->msa_isselector)
		tableinputs |= LOCALISSELECT;

	return tableinputs;
#undef		VALIDLOCALKEY		
#undef		CACHEDPEERKEY		
#undef 		CONNTOMKDPEER		
#undef		CONNTOMKDLOCAL		
#undef		LOCALISSELECT		
}

/**
 * ieee80211_plm_pmkselection - PMK selection procedure
 * @ni: index of cached pmks
 * @msa_parm: structure that stores all the inputs
 * Returns: 0 on success
 */
static int 
ieee80211_plm_pmkselection(struct ieee80211_node *ni, struct ieee80211_msaparms *msa_parm)
{
	u_int8_t initmsa = 0;
	u_int8_t ret, ctmkd;
	u_int32_t tableinputs;
	u_int8_t *pmk;
	/* The key selection procedure first determines if Initial MSA 
	Authentication shall occur. */
	/* XXXPLM: The PMKID list entry in the received peer link open message
	is empty; or, */
	/* The local MP requests authentication during this MSA 
	authentication mechanism; or, */
	if(msa_parm->msa_handshakectl)
		initmsa = 1;
	/* XXXPLM: No PMK-MA(local) is currently valid to secure the link with
	the candidate peer MP; or,*/
	/* The MKDD-ID values included in the received peer link open 
	message and included by the local MP in its Beacon frames and 
	Probe Response frames are different.*/
	ret = memcmp(msa_parm->msc_mkddid, msa_parm->msc_pmkddid, IEEE80211_ADDR_LEN);
	if (ret)
		initmsa = 1;

	/* If initial msa authentication shall happen */
	if (initmsa) {
		/* If the key selection procedure resulted in an indication 
		that Initial MSA Authentication shall occur, the "Connected 
		to MKD" bits contained in the received peer link open message
		and as set by the local MP in its Beacon frames and Probe 
		Response framess shall be examined.  If both MPs have 
		"Connected to MKD" bits set to zero, an OPN_RJCT event 
		(see 11A.1.5.2.3) shall be triggered in order to close the 
		link, with ReasonCode set to MESH-SECURITY-AUTHENTICATION-
		IMPOSSIBLE, since authentication cannot occur.*/
		ctmkd = (msa_parm->msc_msecconf | msa_parm->msc_pmsecconf) 
			& IEEE80211_MSC_CONNTOMKD;
		if (!ctmkd)
			return 1;
		else {
			memset(msa_parm->msa_chosenpmk, 0, IEEE80211_PMK_LEN);
			return 0;
		}
	}

	/* Key selection begins */
	tableinputs = ieee80211_plm_ksinputs(msa_parm);
	switch (tableinputs) {
		/* no pmk */
		case 00000: case 00001:
			return 1;
			break;
		/* XXXPLM: peer pmk */
		case 00010: case 00011: case 00111: 
		case 01000: case 01001: case 01010: case 01011: 
		case 01100: case 01101: case 01110: case 01111: 
		case 11001: case 11011: case 11101: case 11111:
			/*pmk = findpmk(ni, msa_parm);*/
			pmk = NULL;
			memcpy(msa_parm->msa_chosenpmk, pmk, IEEE80211_PMK_LEN);
			break;
		/* XXXPLM: local pmk */
		case 00100: case 00101: case 00110:
		case 10000: case 10001: case 10010: case 10011: 
		case 10100: case 10101: case 10110: case 10111: 
		case 11000: case 11010: case 11100: case 11110:
			/*pmk = findpmk(ni, msa_parm);*/
			pmk = NULL;
			memcpy(msa_parm->msa_chosenpmk, pmk, IEEE80211_PMK_LEN);
			break;
	}
	return 0;
}

/**
 * ieee80211_plm_roleselection - 802.1x role selection procedure
 * @ni: global data 
 * @msa_parm: structure that stores all the inputs
 * Returns: 0 on success
 */
static int 
ieee80211_plm_roleselection(struct ieee80211_node *ni, struct ieee80211_msaparms *msa_parm)
{
	/* input mask:
	 * 		bit 4: request authentication(peer)
	 * 		bit 3: request authentication(local)
	 * 		bit 2: connect to mkd(peer)
	 * 		bit 1: connect to mkd(local)
	 * 		bit 0: is selector?
	 */
#define		REQAUTHPEER			00010000 
#define		REQAUTHLOCAL		00001000
#define 	CONNTOMKDPEER		00000100
#define		CONNTOMKDLOCAL		00000010
#define		LOCALISSELECT		00000001
	struct ieee80211vap *vap = ni->ni_vap;
	u_int32_t input = 0;
	if (msa_parm->msa_phandshakectl)
		input |= REQAUTHPEER;
	if (msa_parm->msa_handshakectl)
		input |= REQAUTHLOCAL;
	if (msa_parm->msc_pmsecconf & IEEE80211_MSC_CONNTOMKD)
		input |= CONNTOMKDPEER;
	if (msa_parm->msc_msecconf & IEEE80211_MSC_CONNTOMKD)
		input |= CONNTOMKDLOCAL;
	if (msa_parm->msa_isselector)
		input |= LOCALISSELECT;

	switch (input) {
		/* peer */
		case 00000: case 01000: case 10000: case 11000:
		case 00100: case 00101: case 01100: case 01101:
		case 10100: case 10101: case 11100: case 11101:
		case 11110: case 00110: case 01110: case 01111:
			memcpy(msa_parm->msa_maid, ni->ni_macaddr, IEEE80211_ADDR_LEN);
			break;
		/* local */
		case 00001: case 01001: case 10001: case 11001:
		case 00010: case 00011: case 01010: case 01011:
		case 10010: case 10011: case 11010: case 11011:
		case 11111: case 00111: case 10110: case 10111: 
			memcpy(msa_parm->msa_maid, vap->iv_myaddr, IEEE80211_ADDR_LEN);
			break;
	}

	return 0;
#undef		REQAUTHPEER			
#undef		REQAUTHLOCAL		
#undef 		CONNTOMKDPEER		
#undef		CONNTOMKDLOCAL		
#undef		LOCALISSELECT		
}

/* process plm open messages, called by ieee80211_recv_mgmt() */
void 
ieee80211_plm_openprocess(struct ieee80211_node *ni, struct ieee80211_frame *wh,
	u_int8_t *frm, u_int8_t *efrm, int rssi, u_int32_t rstamp)
{
#define CONFIRMRECEIVED		((vap->iv_plm_state.cstate == IEEE80211_PLM_S_CNF_RCVD) || \
		(vap->iv_plm_state.cstate == IEEE80211_PLM_S_ESTAB))
#define	IEEE80211_AID_SET(_vap, _b) \
	((_vap)->iv_aid_bitmap[IEEE80211_AID(_b) / 32] |= \
		(1 << (IEEE80211_AID(_b) % 32)))
#define	IEEE80211_AID_ISSET(_vap, _b) \
	((_vap)->iv_aid_bitmap[IEEE80211_AID(_b) / 32] & (1 << (IEEE80211_AID(_b) % 32)))

	struct ieee80211com *ic = ni->ni_ic;
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_rsnparms *rsn_parm = &ni->ni_rsn;
	struct ieee80211_msaparms *msa_parm = &ni->ni_rsn.msaparms;
	struct ieee80211_elems elems;
	u_int8_t rate;
	u_int16_t capinfo, bintval;
	int ret = 0;

	/* Parse none-ie field */
	/* Ignore category and action code */
	frm += 2;
	/* Capability */
	capinfo = le16toh(*(__le16 *)frm);
	frm += 2;
	/* listen interval */
	bintval = le16toh(*(__le16 *)frm);
	frm += 2;
	
	/* Parse ies */
	ieee80211_plm_parseies(vap, &elems, wh, frm, efrm);

	/* Verify ies */
	ret = ieee80211_plm_verifyopen(vap, &elems, wh, rsn_parm);
	IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);			

	if (CONFIRMRECEIVED) {
		ret = ieee80211_plm_verifyopen1(ni, &elems, wh, rsn_parm);
		IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);
	}

	/* key selection */	
	ret = ieee80211_plm_pmkselection(ni, msa_parm);
	IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_AUTHENTICATION_IMPOSSIBLE);

	if (CONFIRMRECEIVED) {
		ret = memcmp(msa_parm->msa_chosenpmk, msa_parm->msa_pchosenpmk, IEEE80211_PMK_LEN);
		IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);
	}

	/* role selection */
	ret = ieee80211_plm_roleselection(ni, msa_parm);
	IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);

	if (CONFIRMRECEIVED) {
		ret = memcmp(msa_parm->msa_maid, msa_parm->msa_pmaid, IEEE80211_ADDR_LEN);
		IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);
	}

	/* Old association request : rates */
	/* discard challenge after association */
	if (ni->ni_challenge != NULL) {
		FREE(ni->ni_challenge, M_DEVBUF);
		ni->ni_challenge = NULL;
	}
	/* 802.11 spec says to ignore station's privacy bit */
	if ((capinfo & IEEE80211_CAPINFO_ESS) == 0) {
		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
			"deny %s request, capability mismatch 0x%x",
			"assoc", capinfo);
		ieee80211_plm_smstep(ni,OPN_RJCT);
		vap->iv_stats.is_rx_assoc_capmismatch++;
		return;
	}
	rate = ieee80211_setup_rates(ni, elems.rates, elems.xrates,
		IEEE80211_F_DOSORT | IEEE80211_F_DOFRATE |
		IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
	/*
	 * If constrained to 11g-only stations reject an
	 * 11b-only station.  We cheat a bit here by looking
	 * at the max negotiated xmit rate and assuming anyone
	 * with a best rate <24Mb/s is an 11b station.
	 */
	if ((rate & IEEE80211_RATE_BASIC) ||
	    ((vap->iv_flags & IEEE80211_F_PUREG) && rate < 48)) {
		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, wh->i_addr2,
			"deny %s request, rate set mismatch",
			"assoc");
		ieee80211_plm_smstep(ni,OPN_RJCT);
		vap->iv_stats.is_rx_assoc_norate++;
		return;
	}

	if (ni->ni_associd != 0 && 
	    IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan)) {
		if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)
		    != (capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME)) {
			IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ANY, 
				wh->i_addr2,
			    	"deny %s request, short slot time "
				"capability mismatch 0x%x",
			    	"assoc", capinfo);
			ieee80211_plm_smstep(ni,OPN_RJCT);
			vap->iv_stats.is_rx_assoc_capmismatch++;
			return;
		}
	}

	/* XXXPLM: This shall be avoid */
	if ((ni->ni_associd == 0) && (vap->iv_opmode == IEEE80211_M_HOSTAP)) {
		u_int16_t aid;

		KASSERT(vap->iv_aid_bitmap != NULL, ("no aid bitmap"));
		/*
		 * It would be good to search the bitmap
		 * more efficiently, but this will do for now.
		 */
		for (aid = 1; aid < vap->iv_max_aid; aid++)
			if (!IEEE80211_AID_ISSET(vap, aid))
				break;
		if (aid >= vap->iv_max_aid) {
			ieee80211_plm_smstep(ni,OPN_RJCT);
			return;
		}
		ni->ni_associd = aid | 0xc000;

		IEEE80211_LOCK_IRQ(ic);
		IEEE80211_AID_SET(vap, ni->ni_associd);
		vap->iv_sta_assoc++;
		ic->ic_sta_assoc++;
		IEEE80211_UNLOCK_IRQ(ic);
	} 
	
	ni->ni_rssi = rssi;
	ni->ni_rstamp = rstamp;
	ni->ni_last_rx = jiffies;
	ni->ni_intval = IEEE80211_BINTVAL_SANITISE(bintval);
	ni->ni_capinfo = capinfo;
	ni->ni_chan = ic->ic_curchan;
	ni->ni_fhdwell = vap->iv_bss->ni_fhdwell;
	ni->ni_fhindex = vap->iv_bss->ni_fhindex;

	/* process succeeded, kick the fsm */
	ieee80211_plm_smstep(ni,OPN_ACPT);	

	/* save ies */
	if (!CONFIRMRECEIVED) {
		IEEE80211_PLM_SAVEIE(ni->ni_rsn_ie, elems.rsnie);
		IEEE80211_PLM_SAVEIE(ni->ni_msc_ie, elems.mscie);
		IEEE80211_PLM_SAVEIE(ni->ni_msa_ie, elems.msaie);
	}
	return; 
	
#undef CONFIRMRECEIVED
}

/**
 * ieee80211_plm_verifyconfirm - verifying ies in confirm message (open first)
 * @ni: global data
 * @elems: pointers to ies
 * @rsn_parm: local msa configuration
 * @wh: header field of received message
 * Returns: 0 on success
 */
static int 
ieee80211_plm_verifyconfirm(struct ieee80211_node *ni, struct ieee80211_elems *elems, 
	const struct ieee80211_frame *wh, struct ieee80211_rsnparms *rsn_parm)
{
	/*struct ieee80211vap *vap = ni->ni_vap;*/
	struct ieee80211_msaparms *msa_parm = &rsn_parm->msaparms;
	struct ieee80211_ie_msa *msa = (struct ieee80211_ie_msa *)elems->msaie;
	u_int8_t selpairwise, selakm;
	int res = 0;

	res = memcmp(elems->rsnie, ni->ni_rsn_ie, ni->ni_rsn_ie[1]);
	if (res)
		return 1;
	res = memcmp(elems->mscie, ni->ni_msc_ie, ni->ni_msc_ie[1]);
	if (res)
		return 1;

	res = (msa->msa_hsctl == msa_parm->msa_phandshakectl) ? 1 : 0;
	if (res)
		return 1;

	res = memcmp(msa->msa_maid, msa_parm->msa_maid, IEEE80211_ADDR_LEN);
	if (res)
		return 1;

	if (msa_parm->msa_isselector) {
		selpairwise = msa_parm->msa_ucastcipher;
		selakm = msa_parm->msa_keymgmt;
	} else {
		selpairwise = msa_parm->msa_pucastcipher;
		selakm = msa_parm->msa_pkeymgmt;
	}

	res = 0;
	res = selpairwise & (1 << rsn_cipher(msa->msa_selucipher, &rsn_parm->rsn_ucastkeylen));
	if (!res)
		return 1;
	res = selakm & rsn_keymgmt(msa->msa_selakm);
	if (!res)
		return 1;

	return 0;
}

/**
 * ieee80211_plm_verifyconfirm1 - verifying ies in open message (confirm first)
 * @ni: global data
 * @elems: pointers to ies
 * @rsn_parm: local msa configuration
 * @wh: header field of received message
 * Returns: 0 on success
 */
static int 
ieee80211_plm_verifyconfirm1(struct ieee80211_node *ni, struct ieee80211_elems *elems, 
	const struct ieee80211_frame *wh, struct ieee80211_rsnparms *rsn_parm)
{
	/*struct ieee80211vap *vap = ni->ni_vap;*/
	struct ieee80211_msaparms *msa_parm = &rsn_parm->msaparms;
	struct ieee80211_ie_msa *msa = (struct ieee80211_ie_msa *)elems->msaie;
	int res;

	msa_parm->msa_phandshakectl = msa->msa_hsctl;
	memcpy(msa_parm->msa_pmaid, msa->msa_maid, IEEE80211_ADDR_LEN);
	
	msa_parm->msa_pucastcipher = 0;
	msa_parm->msa_pucastcipher |= (1 << rsn_cipher(msa->msa_selucipher, &rsn_parm->rsn_ucastkeylen));
	msa_parm->msa_pkeymgmt = 0;
	msa_parm->msa_pkeymgmt |= rsn_keymgmt(msa->msa_selakm);

	res = 0;
	res = msa_parm->msa_pucastcipher & rsn_parm->rsn_ucastcipherset;
	if (!res)
		return 1;
	res = 0;
	res = msa_parm->msa_pkeymgmt & rsn_parm->rsn_keymgmtset;
	if (!res)
		return 1;

	if (msa_parm->msa_isselector) {
		res = (msa_parm->msa_pucastcipher != msa_parm->msa_ucastcipher)
			|| (msa_parm->msa_pkeymgmt != msa_parm->msa_keymgmt);
		if (res)
			return 1;
	}

	return 0;
}

/* Process confirm messages, called by ieee80211_recv_mgmt() */
void 
ieee80211_plm_confirmprocess(struct ieee80211_node *ni, struct ieee80211_frame *wh,
	u_int8_t *frm, u_int8_t *efrm)
{
#define OPENRECEIVED	vap->iv_plm_state.cstate == IEEE80211_PLM_S_OPN_RCVD

	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = vap->iv_ic;
	struct ieee80211_elems elems;
	struct ieee80211_rsnparms rsn_parm;
	u_int16_t capinfo, associd, status;
	int ret;

	capinfo = le16toh(*(__le16 *)frm);
	frm += 2;
	status = le16toh(*(__le16 *)frm);
	frm += 2;
	if (status != 0) {
		IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_ASSOC,
			wh->i_addr2,
			"%sassoc failed (reason %d)",
			"", status);
		vap->iv_stats.is_rx_auth_fail++;	/* XXX */
		ieee80211_plm_smstep(ni,CNF_RJCT);
		return;
	}
	associd = le16toh(*(__le16 *)frm);
	frm += 2;
	
	ieee80211_plm_parseies(vap, &elems, wh, frm, efrm);
	
	if (OPENRECEIVED){
		ret = ieee80211_plm_verifyconfirm(ni, &elems, wh, &rsn_parm);
		IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);			
	} else {
		ret = ieee80211_plm_verifyconfirm1(ni, &elems, wh, &rsn_parm);
		IEEE80211_VERIFICATION_FAILED(ret, MESH_SECURITY_FAILED_VERIFICATION);	
	}

	ni->ni_capinfo = capinfo;
	if (associd)
		ni->ni_associd = associd;
	ni->ni_flags &= ~IEEE80211_NODE_QOS;

	if (IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
	    ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) &&
	    (ic->ic_caps & IEEE80211_C_SHPREAMBLE))) {
		ic->ic_flags |= IEEE80211_F_SHPREAMBLE;
		ic->ic_flags &= ~IEEE80211_F_USEBARKER;
	} else {
		ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE;
		ic->ic_flags |= IEEE80211_F_USEBARKER;
	}
	ieee80211_set_shortslottime(ic,
		IEEE80211_IS_CHAN_A(ic->ic_curchan) ||
			(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME));
	/*
	 * Honor ERP protection.
	 *
	 * NB: ni_erp should zero for non-11g operation
	 *     but we check the channel characteristics
	 *     just in case.
	 */
	if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) &&
	    (ni->ni_erp & IEEE80211_ERP_USE_PROTECTION))
		ic->ic_flags |= IEEE80211_F_USEPROT;
	else
		ic->ic_flags &= ~IEEE80211_F_USEPROT;

	ieee80211_plm_smstep(ni,CNF_ACPT);

	IEEE80211_PLM_SAVEIE(ni->ni_rsn_ie, elems.rsnie);
	IEEE80211_PLM_SAVEIE(ni->ni_msc_ie, elems.mscie);
	IEEE80211_PLM_SAVEIE(ni->ni_msa_ie, elems.msaie);

	return;

#undef OPENRECEIVED
}

/**
 * ieee80211_plm_add_msc - add msc ie to a frame 
 * called by ieee80211_plm_opensent() and ieee80211_plm_confirmsent()
 * to add local msa parameters stored in vap->iv_bss->ni_rsn.msaparms
 * @frm: pointer to the beginning of msc ie 
 * @ni: local configuration
 * Returns: pointer to the end of msc ie 
 */
static u_int8_t *
ieee80211_plm_add_msc(u_int8_t *frm, struct ieee80211_node *ni)
{
	struct ieee80211_ie_msc *ie = (struct ieee80211_ie_msc *) frm;
	struct ieee80211_msaparms *msaparms = &ni->ni_rsn.msaparms;

	*frm++ = IEEE80211_ELEMID_MSC;
	*frm++ = 0;
	
	memcpy(frm, &msaparms->msc_mkddid, IEEE80211_ADDR_LEN);
	frm += IEEE80211_ADDR_LEN;
	*frm++ = msaparms->msc_msecconf;
	
	ie->msc_len = frm - &ie->msc_mkddid[0];

	return frm;
}

/**
 * ieee80211_plm_add_msa - add msa ie to a frame 
 * called by ieee80211_plm_opensent() and ieee80211_plm_confirmsent()
 * to add local msa parameters stored in vap->iv_bss->ni_rsn.msaparms
 * @frm: pointer to the beginning of msa ie 
 * @ni: local configuration
 * Returns: pointer to the end of msa ie 
 */
static u_int8_t *
ieee80211_plm_add_msa(u_int8_t *frm, struct ieee80211_node *ni)
{
#define	RSN_OUI_BYTES		0x00, 0x0f, 0xac

	static const u_int8_t cipher_suite[][4] = {
		{ RSN_OUI_BYTES, RSN_CSE_WEP40 },	/* NB: 40-bit */
		{ RSN_OUI_BYTES, RSN_CSE_TKIP },
		{ RSN_OUI_BYTES, RSN_CSE_WRAP },
		{ RSN_OUI_BYTES, RSN_CSE_CCMP },
		{ 0x00, 0x00, 0x00, 0x00 },		/* XXX CKIP */
		{ RSN_OUI_BYTES, RSN_CSE_NULL },
	};
	/*static const u_int8_t wep104_suite[4] =
		{ RSN_OUI_BYTES, RSN_CSE_WEP104 };*/
	static const u_int8_t key_mgt_unspec[4] =
		{ RSN_OUI_BYTES, RSN_ASE_8021X_UNSPEC };
	static const u_int8_t key_mgt_psk[4] =
		{ RSN_OUI_BYTES, RSN_ASE_8021X_PSK };
	static const u_int8_t keyholder_trans_eap[4] = 
		{ RSN_OUI_BYTES, MSA_KHT_EAP };
	
	struct ieee80211_ie_msa *ie = (struct ieee80211_ie_msa *) frm;
	const struct ieee80211_msaparms *msaparms = &ni->ni_rsn.msaparms;

	*frm++ = IEEE80211_ELEMID_MSA;
	*frm++ = 0;

	*frm++ = msaparms->msa_handshakectl;

	memcpy(frm, &msaparms->msa_maid, IEEE80211_ADDR_LEN);
	frm += IEEE80211_ADDR_LEN;

	/* selected pairwise cipher suite */
	if(msaparms->msa_isselector) {
		if (msaparms->msa_ucastcipher == IEEE80211_CIPHER_AES_CCM) {
			memcpy(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM], 4);
			frm += 4;
		} else if (msaparms->msa_ucastcipher == IEEE80211_CIPHER_TKIP) {
			memcpy(frm, cipher_suite[IEEE80211_CIPHER_TKIP], 4);
			frm += 4;
		} 
	} else {
		if (msaparms->msa_pucastcipher == IEEE80211_CIPHER_AES_CCM) {
			memcpy(frm, cipher_suite[IEEE80211_CIPHER_AES_CCM], 4);
			frm += 4;
		} else if (msaparms->msa_pucastcipher == IEEE80211_CIPHER_TKIP) {
			memcpy(frm, cipher_suite[IEEE80211_CIPHER_TKIP], 4);
			frm += 4;
		} else {
			memset(frm, 0, 4);
			frm += 4;
		}
	}

	/* selected akm suite */
	if(msaparms->msa_isselector) {
		if (msaparms->msa_keymgmt == WPA_ASE_8021X_UNSPEC) {
			memcpy(frm, key_mgt_unspec, 4);
			frm += 4;
		} else if (msaparms->msa_keymgmt == WPA_ASE_8021X_PSK) {
			memcpy(frm, key_mgt_psk, 4);
			frm += 4;		
		}
	} else {
		if (msaparms->msa_pkeymgmt == WPA_ASE_8021X_UNSPEC) {
			memcpy(frm, key_mgt_unspec, 4);
			frm += 4;
		} else if (msaparms->msa_pkeymgmt == WPA_ASE_8021X_PSK) {
			memcpy(frm, key_mgt_psk, 4);
			frm += 4;		
		} else {
			memset(frm, 0, 4);
			frm += 4;
		}
	}

	/* Key holder transport list */
	/* XXXPLM: fixed length */
	*frm++ = 2;		/* sub-eid: 2 */
	*frm++ = 4;		/* 4 octets in length */
	memcpy(frm, keyholder_trans_eap, 4);
	frm += 4; 

	/* XXXPLM: Ignore other fields */
	ie->msa_len = frm - &ie->msa_hsctl;

	return frm;

#undef RSN_OUI_BYTES
}

/* Send plm open messages, called by IEEE80211_SEND_MGMT macro */
int 
ieee80211_plm_opensent(struct ieee80211_node * ni, 
		struct sk_buff *skb, u_int8_t *frm, int status)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;
	u_int16_t capinfo;
	
	/*
	 * plm open frame format
	 *  [1] category 
	 *  [1] action value
	 *	[2] capability information
	 *	[2] listen interval - XXXPLM: keep compatible with old assoc req
	 *	[tlv] supported rates
	 *	[4] power capability (802.11h)
	 *	[28] supported channels element (802.11h)
	 *	[tlv] extended supported rates
	 *	[tlv] RSN IE 
	 *  [tlv] MSC IE
	 *	[tlv] MSA IE
	 */
	skb = ieee80211_getmgtframe(&frm, 
		2 + sizeof(u_int16_t) + 
		sizeof(u_int16_t) +
		IEEE80211_ADDR_LEN +
		2 + IEEE80211_NWID_LEN +
		2 + IEEE80211_RATE_SIZE +
		/*4 + (2 + IEEE80211_SUPPCHAN_LEN) +*/
		2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) +
		(vap->iv_opt_ie != NULL ? vap->iv_opt_ie_len : sizeof(struct ieee80211_ie_wpa) ) +
		sizeof(struct ieee80211_ie_msc) + 
		sizeof(struct ieee80211_ie_msa) );
	if (skb == NULL)
		return 1;

	*frm++ = PLM_ACTION_CATEGORY;
	*frm++ = PLM_ACTION_CODE_OPEN;
	
	capinfo = 0;
	capinfo |= IEEE80211_CAPINFO_ESS;
	if (vap->iv_flags & IEEE80211_F_PRIVACY)
		capinfo |= IEEE80211_CAPINFO_PRIVACY;
	/*
	 * NB: Some 11a AP's reject the request when
	 *     short premable is set.
	 */
	/* Capability information */
	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
	    (ic->ic_caps & IEEE80211_C_SHSLOT))
		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
	*(__le16 *)frm = htole16(capinfo);
	frm += 2;

	/* listen interval */
	/* XXXPLM: Compatibility */
	*(__le16 *)frm = htole16(ic->ic_lintval);
	frm += 2;
	
	/* supported rates */
	frm = ieee80211_add_rates(frm, &ni->ni_rates);
	/* XXXPLM: power capability/supported channels */
	/*if (ic->ic_flags & IEEE80211_F_DOTH)
		frm = ieee80211_add_doth(frm, ic);*/
	/* ext. supp. rates */
	frm = ieee80211_add_xrates(frm, &ni->ni_rates);

	/* RSN IE */
	/* XXXPLM: eliminate differences between initiator and responder */
	if (vap->iv_opt_ie != NULL) {
		memcpy(frm, vap->iv_opt_ie, vap->iv_opt_ie_len);
		frm += vap->iv_opt_ie_len;
	} else {
		frm = ieee80211_add_wpa(frm, vap);
	}

	/* MSC IE */
	frm = ieee80211_plm_add_msc(frm, ni);
	
	/* MSA IE */
	frm = ieee80211_plm_add_msa(frm, ni);
	
	skb_trim(skb, frm - skb->data);
	return 0;
}

/* Send plm confirm messages, called by IEEE80211_SEND_MGMT macro */
int 
ieee80211_plm_confirmsent(struct ieee80211_node * ni, 
		struct sk_buff *skb, u_int8_t *frm, int status)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;
	u_int16_t capinfo;
	
	/*
	 * plm open frame format
	 *  [1] category 
	 *  [1] action value
	 *	[2] capability information
	 *	[2] status
	 * 	[2] association ID
	 *	[tlv] supported rates
	 *	[tlv] extended supported rates
	 *	[tlv] RSN IE 
	 *  [tlv] MSC IE
	 *	[tlv] MSA IE
	 */
	skb = ieee80211_getmgtframe(&frm, 
		2 + 3 * sizeof(u_int16_t) + 
		2 + IEEE80211_RATE_SIZE +
		/*4 + (2 + IEEE80211_SUPPCHAN_LEN) +*/
		2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) +
		(vap->iv_opt_ie != NULL ? vap->iv_opt_ie_len : sizeof(struct ieee80211_ie_wpa) ) +
		sizeof(struct ieee80211_ie_msc) + 
		sizeof(struct ieee80211_ie_msa) );
	if (skb == NULL)
		return 1;

	*frm++ = PLM_ACTION_CATEGORY;
	*frm++ = PLM_ACTION_CODE_CONFIRM;

	/* Capability Information */
	capinfo = 0;
	capinfo |= IEEE80211_CAPINFO_ESS;
	if (vap->iv_flags & IEEE80211_F_PRIVACY)
		capinfo |= IEEE80211_CAPINFO_PRIVACY;
	/*
	 * NB: Some 11a AP's reject the request when
	 *     short premable is set.
	 */
	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
	    IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan))
		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
	if ((ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_SLOTTIME) &&
	    (ic->ic_caps & IEEE80211_C_SHSLOT))
		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
	*(__le16 *)frm = htole16(capinfo);
	frm += 2;

	/* status */
	*(__le16 *)frm = htole16(status);
	frm += 2;

	/* Assoc ID */
	if (status == IEEE80211_STATUS_SUCCESS) {
		*(__le16 *)frm = htole16(ni->ni_associd);
		IEEE80211_NODE_STAT(ni, tx_assoc);
	} else
		IEEE80211_NODE_STAT(ni, tx_assoc_fail);
	frm += 2;
	
	/* supported rates */
	frm = ieee80211_add_rates(frm, &ni->ni_rates);
	/* XXXPLM: power capability/supported channels */
	/*if (ic->ic_flags & IEEE80211_F_DOTH)
		frm = ieee80211_add_doth(frm, ic);*/
	/* ext. supp. rates */
	frm = ieee80211_add_xrates(frm, &ni->ni_rates);

	/* RSN IE */
	/* XXXPLM: eliminate differences between initiator and responder */
	if (vap->iv_opt_ie != NULL) {
		memcpy(frm, vap->iv_opt_ie, vap->iv_opt_ie_len);
		frm += vap->iv_opt_ie_len;
	} else {
		frm = ieee80211_add_wpa(frm, vap);
	}

	/* MSC IE */
	frm = ieee80211_plm_add_msc(frm, ni);
	
	/* MSA IE */
	frm = ieee80211_plm_add_msa(frm, ni);
	
	skb_trim(skb, frm - skb->data);
	return 0;
}

/**
 * ieee80211_plm_smtimer_setr1 - wrapper used to schedule retry timer r1
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: NULL
 */
static void
ieee80211_plm_smtimer_setr1(struct ieee80211_node *ni)
{	
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	sm->retrytimer1.function = ieee80211_plm_tor1handler;
	sm->retrytimer1.data = (unsigned long)ni;
	mod_timer(&sm->retrytimer1, jiffies + IEEE80211_PLM_TOR1 * HZ);
}

/**
 * ieee80211_plm_smtimer_setr2 - wrapper used to schedule retry timer r2
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: NULL
 */
static void
ieee80211_plm_smtimer_setr2(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	sm->retrytimer2.function = ieee80211_plm_tor2handler;
	sm->retrytimer2.data = (unsigned long)ni;
	mod_timer(&sm->retrytimer2, jiffies + (IEEE80211_PLM_TOR1 * HZ) * IEEE80211_PLM_MAXRETRIES);
}

/**
 * ieee80211_plm_smtimer_clrr - wrapper used to delete retry timers
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: 0 on success
 */
static int
ieee80211_plm_smtimer_clrr(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	del_timer(&sm->retrytimer1);
	del_timer(&sm->retrytimer2);
	return 0;
}

/**
 * ieee80211_plm_smtimer_setc - wrapper used to schedule confirm timer
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: NULL
 */
static void
ieee80211_plm_smtimer_setc(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	sm->comfirmtimer.function = ieee80211_plm_tochandler;
	sm->comfirmtimer.data = (unsigned long)ni;
	mod_timer(&sm->comfirmtimer, jiffies + IEEE80211_PLM_TOC * HZ);
}

/**
 * ieee80211_plm_smtimer_clrc - wrapper used to delete confirm timer
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: 0 on success
 */
static int
ieee80211_plm_smtimer_clrc(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	del_timer(&sm->comfirmtimer);
	return 0;
}

/**
 * ieee80211_plm_smtimer_seth - wrapper used to schedule holding timer
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: NULL
 */
static void
ieee80211_plm_smtimer_seth(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	sm->holdingtimer.function = ieee80211_plm_tohhandler;
	sm->holdingtimer.data = (unsigned long)ni;
	mod_timer(&sm->holdingtimer, jiffies + IEEE80211_PLM_TOH * HZ);
}

/**
 * ieee80211_plm_smtimer_clrh - wrapper used to delete holding timer
 * @ni: pointer to ieee80211_plmsm structure
 * Returns: 0 on success
 */
/*static int
ieee80211_plm_smtimer_clrh(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;
	del_timer(&sm->holdingtimer);
	return 0;
}*/

/**
 * ieee80211_plm_tor1handler - tor1 timeout handler
 * @arg: lone argument to the handler
 * Returns: NULL
 */
void 
ieee80211_plm_tor1handler(unsigned long arg)
{
	struct ieee80211_node *ni = (struct ieee80211_node *) arg;
	ieee80211_plm_smstep(ni, TOR1);
}

/**
 * ieee80211_plm_tor2handler - tor1 timeout handler
 * @arg: lone argument to the handler
 * Returns: NULL
 */
void 
ieee80211_plm_tor2handler(unsigned long arg)
{
	struct ieee80211_node *ni = (struct ieee80211_node *) arg;
	ieee80211_plm_smstep(ni, TOR2);
}

/**
 * ieee80211_plm_tochandler - tor1 timeout handler
 * @arg: lone argument to the handler
 * Returns: NULL
 */
void 
ieee80211_plm_tochandler(unsigned long arg)
{
	struct ieee80211_node *ni = (struct ieee80211_node *) arg;
	ieee80211_plm_smstep(ni, TOC);
}

/**
 * ieee80211_plm_tohhandler - tor1 timeout handler
 * @arg: lone argument to the handler
 * Returns: NULL
 */
void 
ieee80211_plm_tohhandler(unsigned long arg)
{
	struct ieee80211_node *ni = (struct ieee80211_node *) arg;
	ieee80211_plm_smstep(ni, TOH);
}

void 
ieee80211_plm_assocsuccess(struct ieee80211_node *ni)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211com *ic = ni->ni_ic;

	/* XXXPLM: This shall be avoid */
	switch (vap->iv_opmode) {
		case IEEE80211_M_HOSTAP:
			/* Send TGf L2UF frame on behalf of newly associated station */
			ieee80211_deliver_l2uf(ni);
			/* give driver a chance to setup state like ni_txrate */
			if (ic->ic_newassoc != NULL)
				ic->ic_newassoc(ni, 1);
			ni->ni_inact_reload = vap->iv_inact_auth;
			ni->ni_inact = ni->ni_inact_reload;
			/* tell the authenticator about new station */
			if (vap->iv_auth->ia_node_join != NULL)
				vap->iv_auth->ia_node_join(ni);
			ieee80211_notify_node_join(ni, 1);
			break;
		case IEEE80211_M_STA:
			ieee80211_new_state(vap, IEEE80211_S_RUN, IEEE80211_FC0_SUBTYPE_ASSOC_RESP);
			break;
		default:
			break;
	}	
}

/**
 * _ieee80211_plm_smstep - PLM FSM state transitions
 * @ni: global data, pointer to ieee80211_plmsm structure and destination node infomation
 * @signal: trigger event 
 * Returns: 0 on success
 */
static int
_ieee80211_plm_smstep(struct ieee80211_node *ni, enum ieee80211_plm_smevent signal)
{
	struct ieee80211vap *vap = ni->ni_vap;
	struct ieee80211_plmsm *sm = &vap->iv_plm_state;

	enum ieee80211_plm_state ostate = sm->cstate;
	enum ieee80211_plm_state nstate = 0;
	int invalidtran = 0;	/* set if none specified transition happens*/
	
	del_timer(&vap->iv_mgtsend); /* took over by plm timer from now */

	switch (ostate) {
		case IEEE80211_PLM_S_IDLE:
			switch (signal) {
				case ACTOPN:
					nstate = IEEE80211_PLM_S_OPN_SNT;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_OPEN);
					ieee80211_plm_smtimer_setr1(ni);
					ieee80211_plm_smtimer_setr2(ni);
					break;
				case PASOPN:
					nstate = IEEE80211_PLM_S_LISTEN;
					/* XXXPLM: no ACTOPN, should we add a listen timer to override? */
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break; 
			}
			break;
		case IEEE80211_PLM_S_LISTEN:
			switch (signal) {
				case ACTOPN: 
					nstate = IEEE80211_PLM_S_OPN_SNT;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_OPEN);
					ieee80211_plm_smtimer_setr1(ni);
					break;
				case OPN_ACPT:
					nstate = IEEE80211_PLM_S_OPN_RCVD;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_OPEN);
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CONFIRM);
					ieee80211_plm_smtimer_setr1(ni);
					ieee80211_plm_smtimer_setr2(ni);
					break;
				case CNCL:
					nstate = IEEE80211_PLM_S_IDLE;
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break; 
			}
			break;
		case IEEE80211_PLM_S_OPN_SNT:
			switch (signal) {
				case OPN_ACPT:
					nstate = IEEE80211_PLM_S_OPN_RCVD;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CONFIRM);
					break;
				case CNF_ACPT:
					nstate = IEEE80211_PLM_S_CNF_RCVD;
					ieee80211_plm_smtimer_clrr(ni);
					ieee80211_plm_smtimer_setc(ni);
					break;
				case TOR1:
					nstate = IEEE80211_PLM_S_OPN_SNT;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_OPEN);
					ieee80211_plm_smtimer_setr1(ni);
					break;
				case CLS_ACPT:
				case OPN_RJCT:
				case CNF_RJCT:
				case TOR2:
				case CNCL:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					ieee80211_plm_smtimer_clrr(ni);
					ieee80211_plm_smtimer_seth(ni);
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break; 
				}
			break;
		case IEEE80211_PLM_S_OPN_RCVD:
			switch (signal) {
				case CNF_ACPT:
					nstate = IEEE80211_PLM_S_ESTAB;
					ieee80211_plm_assocsuccess(ni);
					ieee80211_plm_smtimer_clrr(ni);
					break;
				case TOR1:
					nstate = IEEE80211_PLM_S_OPN_RCVD;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_OPEN);
					ieee80211_plm_smtimer_setr1(ni);
					break;
				case CLS_ACPT:
				case OPN_RJCT:
				case CNF_RJCT:
				case TOR2:
				case CNCL:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					ieee80211_plm_smtimer_clrr(ni);
					ieee80211_plm_smtimer_seth(ni);
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break;
				}
			break; 
		case IEEE80211_PLM_S_CNF_RCVD:
			switch (signal) {
				case OPN_ACPT:
					nstate = IEEE80211_PLM_S_ESTAB;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CONFIRM);
					ieee80211_plm_assocsuccess(ni);
					ieee80211_plm_smtimer_clrc(ni);
					break;
				case CNF_ACPT:
					nstate = IEEE80211_PLM_S_CNF_RCVD;
					break;
				case TOC:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					ieee80211_plm_smtimer_seth(ni);
					break;
				case CLS_ACPT:
				case OPN_RJCT:
				case CNF_RJCT:
				case CNCL:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					ieee80211_plm_smtimer_clrc(ni);
					ieee80211_plm_smtimer_seth(ni);
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break;
				}
			break;
		case IEEE80211_PLM_S_ESTAB:
			switch (signal) {
				case CLS_ACPT:
				case OPN_RJCT:
				case CNF_RJCT:
				case CNCL:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					ieee80211_plm_smtimer_seth(ni);
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break;
				}
			break;
		case IEEE80211_PLM_S_HOLDING:
			switch (signal) {
				case OPN_ACPT:
				case CNF_ACPT:
				case OPN_RJCT:
				case CNF_RJCT:
					nstate = IEEE80211_PLM_S_HOLDING;
					/*IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_ACTION, 
						PLM_ACTION_CODE_CLOSE);*/
					break;
				case TOH:
				case CLS_ACPT:
					nstate = IEEE80211_PLM_S_IDLE;
					IEEE80211_SEND_MGMT(ni,
						IEEE80211_FC0_SUBTYPE_DEAUTH,
						IEEE80211_REASON_RSN_REQUIRED);
					break;
				default: 
					invalidtran = 1;
					IEEE80211_DPRINTF(vap, IEEE80211_MSG_ANY,
					"%s: invalid transition\n", __func__);
					break;
				}
			break;
	}

	if (!invalidtran) {
		IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__,
			ieee80211_plmstate_name[ostate], ieee80211_plmstate_name[nstate]);
		sm->pstate = ostate;
		sm->cstate = nstate; 		/* finish state transition */
	}
	
	return 0;
}

/* Kick the plm state machine */
int 
ieee80211_plm_smstep(struct ieee80211_node *ni, enum ieee80211_plm_smevent signal)
{
	/* XXXPLM: be cautious, to use ni->ni_ic */
	/*struct ieee80211vap *vap = ni->ni_vap;*/
	struct ieee80211com *ic = ni->ni_ic;
	int rc;

	/* grab the lock so that only one vap can go through transition at any time */
	IEEE80211_VAPS_LOCK_BH(ic);
	rc = _ieee80211_plm_smstep(ni,signal);
	IEEE80211_VAPS_UNLOCK_BH(ic);
	return rc;
}


